package com.geccocrawler.gecco.spider.render.html;

import com.geccocrawler.gecco.annotation.*;
import com.geccocrawler.gecco.request.HttpRequest;
import com.geccocrawler.gecco.response.HttpResponse;
import com.geccocrawler.gecco.spider.SpiderBean;
import com.geccocrawler.gecco.spider.conversion.Conversion;
import com.geccocrawler.gecco.spider.render.Render;
import com.geccocrawler.gecco.spider.render.RenderContext;
import com.geccocrawler.gecco.spider.render.RenderType;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.parser.Parser;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HtmlParser {

    private Logger log = LoggerFactory.getLogger(getClass());

    private Document document;

    private String baseUri;

    public HtmlParser(String baseUri, String content) {
        long beginTime = System.currentTimeMillis();
        this.baseUri = baseUri;
        if (isTable(content)) {
            this.document = Jsoup.parse(content, baseUri, Parser.xmlParser());
        } else {
            this.document = Jsoup.parse(content, baseUri);
        }
        long endTime = System.currentTimeMillis();
        if (log.isTraceEnabled()) {
            log.trace("init html parser : " + (endTime - beginTime) + "ms");
        }
    }

    public String baseUri() {
        return baseUri;
    }

    public Object $basic(String selector, Field field) throws Exception {
        if (field.isAnnotationPresent(Text.class)) {// @Text
            Text text = field.getAnnotation(Text.class);
            String value = $text(selector, text.own());
            return Conversion.getValue(field.getType(), value);
        } else if (field.isAnnotationPresent(Image.class)) {// @Image
            Image image = field.getAnnotation(Image.class);
            String imageSrc = $image(selector, image.value());
            /*String localPath = DownloadImage.download(image.download(), imageSrc);
            if (StringUtils.isNotEmpty(localPath)) {
				return localPath;
			}*/
            return imageSrc;
        } else if (field.isAnnotationPresent(Href.class)) {// @Href
            Href href = field.getAnnotation(Href.class);
            String url = $href(selector, href.value());
            return url;
        } else if (field.isAnnotationPresent(Attr.class)) {// @Attr
            Attr attr = field.getAnnotation(Attr.class);
            String name = attr.value();
            return Conversion.getValue(field.getType(), $attr(selector, name));
        } else if (field.isAnnotationPresent(Html.class)) {// @Html
            Html html = field.getAnnotation(Html.class);
            return $html(selector, html.outer());
        } else {// @Html
            return $html(selector);
        }
    }

    public List<Object> $basicList(String selector, Field field) throws Exception {
        List<Object> list = new ArrayList<Object>();
        Elements els = $(selector);
        for (Element el : els) {
            if (field.isAnnotationPresent(Text.class)) {// @Text
                Text text = field.getAnnotation(Text.class);
                list.add(Conversion.getValue(field.getType(), $text(el, text.own())));
            } else if (field.isAnnotationPresent(Image.class)) {// @Image
                Image image = field.getAnnotation(Image.class);
                String imageSrc = $image(el, image.value());
                /*String localPath = DownloadImage.download(image.download(), imageSrc);
                if (StringUtils.isNotEmpty(localPath)) {
					list.add(localPath);
				}*/
                list.add(imageSrc);
            } else if (field.isAnnotationPresent(Href.class)) {// @Href
                Href href = field.getAnnotation(Href.class);
                String url = $href(el, href.value());
                list.add(url);
            } else if (field.isAnnotationPresent(Attr.class)) {// @Attr
                Attr attr = field.getAnnotation(Attr.class);
                String name = attr.value();
                list.add(Conversion.getValue(field.getType(), $attr(el, name)));
            } else if (field.isAnnotationPresent(Html.class)) {// @Html
                Html html = field.getAnnotation(Html.class);
                list.add(html.outer() ? el.outerHtml() : el.html());
            } else {// Other
                list.add(el.html());
            }
        }
        return list;
    }

    public SpiderBean $bean(String selector, HttpRequest request, Class<? extends SpiderBean> clazz) {
        String subHtml = $html(selector);
        // table
        HttpResponse subResponse = HttpResponse.createSimple(subHtml);
        Render render = RenderContext.getRender(RenderType.HTML);
        return render.inject(clazz, request, subResponse);
    }

    public List<SpiderBean> $beanList(String selector, HttpRequest request, Class<? extends SpiderBean> clazz) {
        List<SpiderBean> list = new ArrayList<SpiderBean>();
        List<String> els = $list(selector);
        for (String el : els) {
            // table
            HttpResponse subResponse = HttpResponse.createSimple(el);
            Render render = RenderContext.getRender(RenderType.HTML);
            SpiderBean subBean = render.inject(clazz, request, subResponse);
            list.add(subBean);
        }
        return list;
    }

    public Elements $(String selector) {
        Elements elements = document.select(selector);
        if (!selector.equalsIgnoreCase("script"))
            log.debug("[" + selector + "]--->[" + elements + "]");
        return elements;
    }

    public Element $element(String selector) {
        Elements elements = $(selector);
        if (elements != null && elements.size() > 0) {
            return elements.first();
        }
        return null;
    }

    public List<String> $list(String selector) {
        List<String> list = Lists.newArrayList();
        Elements elements = $(selector);
        if (elements != null) {
            for (Element ele : elements) {
                list.add(ele.outerHtml());
            }
        }
        return list;
    }

    public String $html(String selector) {
        return $html(selector, false);
    }

    public String $html(String selector, boolean isOuter) {
        Elements elements = $(selector);
        if (elements != null) {
            if (isOuter) {
                return elements.outerHtml();
            }
            return elements.html();
        }
        return null;
    }

    public String $text(Element element, boolean own) {
        if (element == null) {
            return null;
        }
        String text;
        if (own) {
            text = element.ownText();
        } else {
            text = element.text();
        }
        // 替换掉空格信息
        return StringUtils.replace(text, "\u00A0", "");
    }

    public String $text(String selector, boolean own) {
        Element element = $element(selector);
        if (element != null) {
            return $text(element, own);
        }
        return null;
    }

    public String $attr(Element element, String attr) {
        if (element == null) {
            return null;
        }
        return element.attr(attr);
    }

    public String $attr(String selector, String attr) {
        Element element = $element(selector);
        if (element == null) {
            return null;
        }
        return element.attr(attr);
    }

    public String $href(Element href, String attr) {
        if (href == null) {
            return null;
        }
        return href.absUrl(attr);
    }

    public String $href(Element href, String... attrs) {
        if (href == null) {
            return null;
        }
        for (String attr : attrs) {
            String value = $href(href, attr);
            if (StringUtils.isNotEmpty(value)) {
                return value;
            }
        }
        return $href(href, "href");
    }

    public String $href(String selector, String attr) {
        return $href($element(selector), attr);
    }

    public String $href(String selector, String... attrs) {
        return $href($element(selector), attrs);
    }

    public String $image(Element img, String attr) {
        if (img == null) {
            return null;
        }
        return img.absUrl(attr);
    }

    public String $image(Element img, String... attrs) {
        if (img == null) {
            return null;
        }
        for (String attr : attrs) {
            String value = $image(img, attr);
            if (StringUtils.isNotEmpty(value)) {
                return value;
            }
        }
        return $image(img, "src");
    }

    public String $image(String selector, String attr) {
        return $image($element(selector), attr);
    }

    public String $image(String selector, String... attrs) {
        return $image($element(selector), attrs);
    }


    private boolean isTable(String content) {
        if (!StringUtils.contains(content, "</html>")) {
            String rege = "<\\s*(thead|tbody|tr|td|th)[\\s\\S]+";
            Pattern pattern = Pattern.compile(rege);
            Matcher matcher = pattern.matcher(content);
            if (matcher.matches()) {
                return true;
            }
        }
        return false;
    }
}
